package org.grayrabbit.cms.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.lang.StringUtils;

public class ZipUtil {
	/**
	 * 解压文件
	 * 
	 * @param zipPath
	 *            压缩包路径+文件名
	 * @param unzipPath
	 *            解压路径
	 * @param encode
	 *            文件编码
	 * @param suffix
	 *            留下文件的后缀名,不包括"." 为空不验证
	 * @param size
	 *            过滤文件的最大大小 -1 不验证
	 * @throws IOException
	 * @throws ArchiveException
	 */
	public static List<File> doZip(String zipPath, String unzipPath, String encode, String[] suffix, long size) throws IllegalArgumentException, IOException {
		if(StringUtils.isBlank(zipPath)){
			throw new IllegalArgumentException("Zip文件名错误");
		}
		String fix = zipPath.substring(zipPath.length()-4);
		if (!".zip".equalsIgnoreCase(fix)) {
			throw new IllegalArgumentException("Zip文件名错误");
		}
		File fp = new File(unzipPath); 
		if(!fp.exists()) fp.mkdirs();
		ZipFile zipFile = new ZipFile(zipPath, encode);
		List<File> list = new ArrayList<File>();
		@SuppressWarnings("unchecked")
		Enumeration<ZipArchiveEntry> e = zipFile.getEntries();
		List<ZipArchiveEntry> entries = new ArrayList<ZipArchiveEntry>();
		while (e.hasMoreElements()) {
			ZipArchiveEntry entry = (ZipArchiveEntry) e.nextElement();
			if (entry.isDirectory()) {
				new File(unzipPath,entry.getName()).mkdirs();
			} else {
				if(agreeFile(entry.getName(), suffix)){
					if(size==-1 ||( size>=0&&entry.getSize()<size)){
						entries.add(entry);// 将文件条目保留到列表中
					}
				}
			}
		}
		for (ZipArchiveEntry entry : entries) {
			File f = new File(unzipPath,entry.getName());			
			BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(f));
			InputStream inputStream = zipFile.getInputStream(entry);
			byte[] b = new byte[1024 * 1024];
			int len=0;
			while(( len= inputStream.read(b))!=-1){
				outputStream.write(b, 0, len);
			}
			outputStream.close();
			list.add(f);
		}

		zipFile.close();
		return list;
	}
	
	/**
	 * 解压文件
	 * 
	 * @param zipPath
	 *            压缩包路径+文件名
	 * @param unzipPath
	 *            解压路径
	 * @param encode
	 *            文件编码
	 * @param suffix
	 *            留下文件的后缀名,不包括"." 为空不验证
	 * @param size
	 *            过滤文件的最大大小 -1 不验证
	 * @throws IOException
	 * @throws ArchiveException
	 */
	public static void extractZip(String zipPath, String unzipPath, String encode, String[] suffix, long size) throws IllegalArgumentException, IOException {
		if(StringUtils.isBlank(zipPath)){
			throw new IllegalArgumentException("Zip文件名错误");
		}
		String fix = zipPath.substring(zipPath.length()-4);
		if (!".zip".equalsIgnoreCase(fix)) {
			throw new IllegalArgumentException("Zip文件名错误");
		}
		File fp = new File(unzipPath); 
		if(!fp.exists()) fp.mkdirs();
		ZipFile zipFile = new ZipFile(zipPath, encode);
		@SuppressWarnings("unchecked")
		Enumeration<ZipArchiveEntry> e = zipFile.getEntries();
		List<ZipArchiveEntry> entries = new ArrayList<ZipArchiveEntry>();
		while (e.hasMoreElements()) {
			ZipArchiveEntry entry = (ZipArchiveEntry) e.nextElement();
			if (entry.isDirectory()) {
				new File(unzipPath,entry.getName()).mkdirs();
			} else {
				if(agreeFile(entry.getName(), suffix)){
					if(size==-1 ||( size>=0&&entry.getSize()<size)){
						entries.add(entry);// 将文件条目保留到列表中
					}
				}
			}
		}
		for (ZipArchiveEntry entry : entries) {
			File f = new File(unzipPath,entry.getName());			
			BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(f));
			InputStream inputStream = zipFile.getInputStream(entry);
			byte[] b = new byte[1024 * 1024];
			int len=0;
			while(( len= inputStream.read(b))!=-1){
				outputStream.write(b, 0, len);
			}
			outputStream.close();
		}
		zipFile.close();		 
	}
	
	private static boolean agreeFile(String filename, String[] suffix) {
		// 判断后缀名，如果为空，表示不过滤
		if (suffix == null || suffix.length == 0) {
			return true;
		}
		// 在有判断的情况下，不存在后缀名的文件,不过滤
		if (filename.lastIndexOf(".") == -1) {
			return false;
		}
		String ext = filename.substring(filename.lastIndexOf(".") + 1);
		for (String e : suffix) {
			if (e.equalsIgnoreCase(ext)) {//满足条件的不过滤
				return true;
			}
		}
		return false;
	}
	
	/**
	 * @param source   源头
	 * @param target  目的
	 * @throws Exception
	 */
	public static void createZip(String source, String target)
			throws Exception {
		File folderObject = new File(source);
		
		if (folderObject.exists()) {
			List<File> fileList = getSubFiles(new File(source));
			// 压缩文件名
			ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(
					target));
			ZipEntry ze = null;
			byte[] buf = new byte[1024];
			int readLen = 0;
			for (int i = 0; i < fileList.size(); i++) {
				File f = (File) fileList.get(i);
				// 创建一个ZipEntry，并设置Name和其它的一些属性
				ze = new ZipEntry(getAbsFileName(source, f));
				ze.setSize(f.length());
				ze.setTime(f.lastModified());
				// 将ZipEntry加到zos中，再写入实际的文件内容
				zos.putNextEntry(ze);
				InputStream is = new BufferedInputStream(new FileInputStream(f));
				while ((readLen = is.read(buf, 0, 1024)) != -1) {
					zos.write(buf, 0, readLen);
				}
				is.close(); 
			}
			zos.close();
		} else {
			throw new Exception("this folder isnot exist!");
		}
	}
	
	private static List<File> getSubFiles(File baseDir) {
		List<File> ret = new ArrayList<File>(); 
		File[] tmp = baseDir.listFiles();
		for (int i = 0; i < tmp.length; i++) {
			if (tmp[i].isFile()) {
				ret.add(tmp[i]);
			}
			if (tmp[i].isDirectory()) {
				ret.addAll(getSubFiles(tmp[i]));
			}
		}
		return ret;
	}

	private static String getAbsFileName(String baseDir, File realFile) {
		File real = realFile;
		File base = new File(baseDir).getParentFile();
		String ret = real.getName();
		while (true) {
			real = real.getParentFile();
			if (real == null)
				break;
			if (real.equals(base))
				break;
			else {
				ret = real.getName() + "/" + ret;
			}
		}
		return ret;
	}
}
